# -*- coding: utf-8 -*-

from task.Task import TaskHandle
from network.ClientBase import ClientBase
from rpc.AsynCallback import AsyncTaskManager


class Client(TaskHandle, ClientBase):
    """
    客户端的主要任务是：
    1. 将类中的非私有函数 提取出来作为rpc函数
    2. 当有函数调用事件时，就创建一个task并发送给对应的服务端
    """

    def __init__(self, cls):
        super(Client, self).__init__()
        self._cls_name = cls.__name__
        self._last_called = None
        self._methods = self._extra_methods(cls)
        self._proc_thread = None

    def connect(self, ip='localhost', port=3154):
        super(Client, self).connect(ip, port)
        if self._proc_thread:
            self._proc_thread.stop()
            self._proc_thread = None
        self._proc_thread = Client.process(self)

    def _extra_methods(self, cls):
        if isinstance(cls, dict):
            return cls
        client_methods = set(k for k in dir(self) if not k.startswith('_'))
        return dict((k, getattr(cls, k)) for k in dir(cls)
                    if callable(getattr(cls, k)) and not k.startswith('_') and k not in client_methods)

    @AsyncTaskManager
    def process(self):
        super(Client, self).process()
        _events = []
        while self.has_events:
            event = self.get_next_event()
            data = event[1]
            _events.append(self.unpack_respond(data))
        return _events

    def __getattr__(self, attr):
        if attr in self._methods:
            # 返回一个东西收集参数
            self._last_called = attr
            return self
        else:
            return object.__getattribute__(self, attr)

    def __call__(self, *args, **kwargs):
        if self._last_called is None:
            raise RuntimeError('no rpc method is initialized')
        if self._last_called:
            tid = self.add_task(self._last_called, *args, **kwargs)
            self._last_called = None
            self.send(self.pack_task(tid))
            return self._handle_response(tid)
        else:
            raise RuntimeError('{} is not considered to be a rpc method'.format(self._last_called))

    @AsyncTaskManager.respond
    def _handle_response(self, tid):
        """ 处理有返回值的情况
        会阻塞线程直至收到返回值
        """
        task = self.pop_task(tid)
        if task.callback:
            task.callback()
        return self.pop_respond(tid)

    @AsyncTaskManager.callback
    def _handle_call_back(self, tid):
        """ 处理有回调函数的调用
        callback会等tid事件调用成功之后 才会回调，且不会有返回值
        """
        task = self.pop_task(tid)
        if task.callback:
            task.callback()

    def close(self):
        self._proc_thread.stop()
        self._proc_thread = None
        super(Client, self).close()
